package io.filecopy;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class XMLSerializer {

  public XMLSerializer(){
    
  }
  
  public Document toDoc(JobDescription... descriptions) throws ParserConfigurationException{
    DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
    Document document = documentBuilder.newDocument();
    document.setXmlStandalone(true);
    document.setXmlVersion("1.0");
    Element filecopyElement = document.createElement("filecopy");
    document.appendChild(filecopyElement);
    Element jobDescriptionsElement = document.createElement("jobDescriptions");
    filecopyElement.appendChild(jobDescriptionsElement);
    for (JobDescription description: descriptions){
      Element jobDescriptionElement = document.createElement("jobDescription");
      jobDescriptionsElement.appendChild(jobDescriptionElement);
      jobDescriptionElement.setAttribute("directoryMode", description.getDirectoryMode().name());
      jobDescriptionElement.setAttribute("lastModifyTimeMode", description.getLastModifyTimeMode().name());
      jobDescriptionElement.setAttribute("replaceMode", description.getReplaceMode().name());
      jobDescriptionElement.setAttribute("synchronizeMode", description.getSynchronizeMode().name());
      Element sourceElement = document.createElement("srcDir");
      Element destinationElement = document.createElement("dstDir");
      jobDescriptionElement.appendChild(sourceElement);
      jobDescriptionElement.appendChild(destinationElement);
      sourceElement.appendChild(document.createTextNode(description.getSourceDirectory().getPath()));
      destinationElement.appendChild(document.createTextNode(description.getDestinationDirectory().getPath()));
    }
    return document;
  }
  
  public void toOutputStream(OutputStream out, JobDescription... jobDescriptions) throws ParserConfigurationException, TransformerException{
    Document doc = toDoc(jobDescriptions);
    TransformerFactory factory = TransformerFactory.newInstance();
    Transformer transformer = factory.newTransformer();
    transformer.setOutputProperty(OutputKeys.INDENT, "yes");
    transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
    transformer.transform(new DOMSource(doc), new StreamResult(out));
  }
  
  public JobDescription[] fromStream(InputStream in, File baseDir) throws SAXException, IOException, ParserConfigurationException, XPathExpressionException{
    DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
    Document doc = documentBuilder.parse(in);
    return fromDoc(doc, baseDir);
  }
  
  public JobDescription[] fromDoc(Document doc, File baseDir) throws XPathExpressionException {
    List<JobDescription> result = new ArrayList<JobDescription>();
    XPath path = XPathFactory.newInstance().newXPath();
    NodeList jobsList = (NodeList) path.evaluate("/filecopy/jobDescriptions/jobDescription", doc, XPathConstants.NODESET);
    for (int i = 0; i < jobsList.getLength(); i++){
      Node jobDescriptionNode = jobsList.item(i);
      String srcString = path.evaluate("srcDir", jobDescriptionNode);
      String dstString = path.evaluate("dstDir", jobDescriptionNode);
      File sourceDir = new File(srcString);
      File destDir = new File(dstString);
      if (!sourceDir.isAbsolute())
        sourceDir = new File(baseDir, srcString);
      if (!destDir.isAbsolute())
        destDir = new File(baseDir, dstString);
      FileFilter filter = null;
      DirectoryMode directoryMode = DirectoryMode.valueOf(path.evaluate("@directoryMode", jobDescriptionNode));
      SynchronizeMode synchronizeMode = SynchronizeMode.valueOf(path.evaluate("@synchronizeMode", jobDescriptionNode));
      ReplaceMode replaceMode = ReplaceMode.valueOf(path.evaluate("@replaceMode", jobDescriptionNode));
      LastModifyTimeMode lastModifyTimeMode = LastModifyTimeMode.valueOf(path.evaluate("@lastModifyTimeMode", jobDescriptionNode));
      JobDescription description = new JobDescriptionImpl(sourceDir, destDir, filter, directoryMode, lastModifyTimeMode, replaceMode, synchronizeMode);
      result.add(description);
    }
    return result.toArray(new JobDescription[result.size()]);
  }

  public static void main(String... args) throws ParserConfigurationException, TransformerException, XPathExpressionException{
    XMLSerializer xml = new XMLSerializer();
    JobDescription description = new JobDescriptionImpl(new File("c:/temp"), new File("../temp"), null, DirectoryMode.ALL_FILES_AND_SUBDIRECTORIES, LastModifyTimeMode.KEEP_OR_SOURCE_TIME, ReplaceMode.REPLACE_DIFFERENT, SynchronizeMode.SYNCHRONIZE_ONLY_SOURCE_ENTRIES);
    xml.toOutputStream(System.out, description);
    Document d = xml.toDoc(description);
    JobDescription[] test = xml.fromDoc(d, new File("."));
    JobDescription copy = test[0];
    System.out.println(copy.getDirectoryMode());
    System.out.println(copy.getLastModifyTimeMode());
    System.out.println(copy.getReplaceMode());
    System.out.println(copy.getSynchronizeMode());
    System.out.println(copy.getSourceDirectory().getPath());
    System.out.println(copy.getDestinationDirectory().getPath());
  }
}